﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using ERF;
using Microsoft.Xna.Framework.Content;
using ERF.Physics;
using System.IO;
using FirstPerson.Objects;
using Microsoft.Xna.Framework;

namespace FirstPerson
{
    /// <summary>
    /// Represents the logical game world.
    /// </summary>
    public class Map : IDisposable
    {
        private float width;
        private float height;
        private float depth;
        private string mapName;
        private string heightMapName;
        private string skyBoxName;
        private Terrain terrain;
        private Skybox skybox;

        private WayPoint[] wayPoints;
        private WayPoint[] spawnPoints;

        /// <summary>
        /// Gets or sets the map name
        /// </summary>
        public string Name
        {
            get { return this.mapName; }
            set { this.mapName = value; }
        }

        /// <summary>
        /// Gets or sets the width of the map.
        /// </summary>
        public float Width
        {
            get { return this.width; }
            set { this.width = value; }
        }

        /// <summary>
        /// Gets or sets the height of the map.
        /// </summary>
        public float Height
        {
            get { return this.height; }
            set { this.height = value; }
        }

        /// <summary>
        /// Gets or sets the depth of the map.
        /// </summary>
        public float Depth
        {
            get { return this.depth; }
            set { this.depth = value; }
        }

        /// <summary>
        /// Gets or sets the name of the heightmap.
        /// </summary>
        public string HeightMapName
        {
            get { return this.heightMapName; }
            set { this.heightMapName = value; }
        }

        /// <summary>
        /// Gets or sets the name of the skybox.
        /// </summary>
        public string SkyBoxName
        {
            get { return this.skyBoxName; }
            set { this.skyBoxName = value; }
        }

        /// <summary>
        /// Gets or sets an array of WayPoints defined for the map.
        /// </summary>
        public WayPoint[] WayPoints
        {
            get { return this.wayPoints; }
            set { this.wayPoints = value; }
        }

        /// <summary>
        /// Gets or sets an array of SpawnPoints defined for the map.
        /// </summary>
        public WayPoint[] SpawnPoints
        {
            get { return this.spawnPoints; }
            set { this.spawnPoints = value; }
        }

        /// <summary>
        /// Gets the SkyBox.
        /// </summary>
        public Skybox Skybox
        {
            get { return this.skybox; }
        }

        /// <summary>
        /// Gets the Terrain.
        /// </summary>
        public Terrain Terrain
        {
            get { return this.terrain; }
        }

        private void Initialize(ERFGame game)
        {
            Vector2 size = new Vector2(this.width, depth);
            // The position of the waypoints are relative, so we need to translate them into absolute coordinates.
            // This is done by multiplying with the size of the map, and then subtracting half the size.
            for (int i = 0; i < this.wayPoints.Length; i++)
            {
                this.wayPoints[i].Position *= size;
                this.wayPoints[i].Position -= new Vector2(width/2f, depth/2f);
            }

            // Load heightmap.
            Texture2D heightmap = game.Content.Load<Texture2D>(string.Format("Textures\\{0}", this.heightMapName));
            Color[] color = new Color[heightmap.Width * heightmap.Height];
            heightmap.GetData<Color>(color);

            double[,] input = new double[heightmap.Width, heightmap.Height];
            Color tempColor;
            for (int y = 0; y < heightmap.Height; y++)
            {
                for (int x = 0; x < heightmap.Width; x++) 
                {
                    tempColor = color[(y * heightmap.Width) + x];
                    input[x, y] = (tempColor.R + tempColor.G + tempColor.B) / (3f*255f);
                }
            }

            // Generate the terrain based on the heightmap.
            TerrainGenerator.GenerateTerrain(game, input, out terrain, this.height, this.width, this.depth);

            this.skybox = new Skybox(game, this.skyBoxName);
            this.skybox.UseBlending = false;
            this.skybox.Blend = 0;
            this.skybox.Position = new Vector3(0, 150, 100);
            this.skybox.Scale = new Vector3(3, 3, 3);
        }

        /// <summary>
        /// Loads a map based on the specified XML file.
        /// </summary>
        /// <param name="game">The game that the map will belong to.</param>
        /// <param name="xml">The XML file containing the data for the map.</param>
        /// <returns></returns>
        public static Map Load(ERFGame game, string xml)
        {
            Map instance;
            System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(Map));
            string path = System.IO.Path.Combine(game.Content.RootDirectory, string.Format("Maps\\{0}.xml", xml));

            using (StreamReader reader = new StreamReader(path))
                instance = (Map)serializer.Deserialize(reader);

            instance.Initialize(game);
            return instance;
        }

        #region IDisposable Members

        public void Dispose()
        {
            this.terrain.Dispose();
            this.terrain = null;
            this.skybox.Dispose();
            this.skybox = null;
        }

        #endregion
    }
}
